﻿using System;
using System.Text;
using System.Threading;

using AzureContrib.WindowsAzure.StorageClient.Tests.Mocks;

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace AzureContrib.WindowsAzure.StorageClient.IntegrationTests
{
	[TestClass]
	public class QueueIntegrationTests
	{
		public const string FiddlerProxyConnectionString = "UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://ipv4.fiddler";

		private CloudQueue cloudQueue;

		[TestInitialize]
		public void CreateQueue()
		{
			// var cloudStorageAccount = CloudStorageAccount.Parse(FiddlerProxyConnectionString);
			var cloudStorageAccount = CloudStorageAccount.DevelopmentStorageAccount;

			var cloudQueueClient = new CloudQueueClient(cloudStorageAccount.QueueEndpoint, cloudStorageAccount.Credentials);

			cloudQueue = cloudQueueClient.GetQueueReference("somequeue");

			DeleteQueue();

			cloudQueue.Create();
		}

		[TestCleanup]
		public void DeleteQueue()
		{
			if (cloudQueue.Exists())
			{
				cloudQueue.Delete();
			}
		}

		[TestMethod]
		public void UpdateMessage()
		{
			var firstMessageText = "A message";

			var cloudQueueMessage = new CloudQueueMessage(firstMessageText);

			cloudQueue.AddMessage(cloudQueueMessage);

			// Get the message the first time.
			cloudQueueMessage = cloudQueue.GetMessage(TimeSpan.FromSeconds(30));

			Assert.AreEqual(firstMessageText, cloudQueueMessage.AsString);
			Assert.AreEqual(1, cloudQueueMessage.DequeueCount);

			var updatedMessageText = "Some other message";

			// Modify the message.
			cloudQueueMessage.SetMessageContent(updatedMessageText);

			// We can now UPDATE the message on the queue and also renew our lease on the message.
			cloudQueue.UpdateMessage(
				cloudQueueMessage /* Our message. */,
				TimeSpan.FromMilliseconds(1) /* The new lease timeout. Very short so that time out happens right away. */,
				MessageUpdateFields.Content | MessageUpdateFields.Visibility /* Flags for what we are updating on the messge. */);

			// Get the message a second time.
			cloudQueueMessage = cloudQueue.GetMessage(TimeSpan.FromSeconds(1));

			Assert.AreEqual(updatedMessageText, cloudQueueMessage.AsString);
			Assert.AreEqual(2, cloudQueueMessage.DequeueCount);
		}

		[TestMethod]
		public void Serialize_CloudQueueMessage_body()
		{
			var messageData = "This is some message data.";

			CloudQueueMessage cloudQueueMessage = new FooWork(messageData).AsMessage();

			Assert.AreEqual("{\"SomeData\":\"This is some message data.\",\"WorkState\":0}", cloudQueueMessage.AsString);

			var fooWork = cloudQueueMessage.Payload<FooWork>();

			Assert.AreEqual(messageData, fooWork.SomeData);
			Assert.AreEqual(WorkState.Step1, fooWork.WorkState);
		}

		[Serializable]
		public struct FooWork
		{
			public FooWork(string messageData)
				: this()
			{
				SomeData = messageData;
				WorkState = WorkState.Step1;
			}

			public string SomeData { get; set; }
			public WorkState WorkState { get; set; }
		}

		public enum WorkState
		{
			Step1,
			Step2,
			Step3
		}

		[TestMethod]
		public void Delayed_AddMessage()
		{
			var message = "A message";
			var oneSecond = TimeSpan.FromSeconds(1);

			var cloudQueueMessage = new CloudQueueMessage(message);

			TimeSpan? timeToLive = null;
			TimeSpan? initialVisibilityDelay = oneSecond;
// ReSharper disable ExpressionIsAlwaysNull
			cloudQueue.AddMessage(cloudQueueMessage, timeToLive, initialVisibilityDelay);
// ReSharper restore ExpressionIsAlwaysNull

			// There is no message when we peek into the queue - it's visibility is delayed.
			Assert.IsNull(cloudQueue.PeekMessage());

			Thread.Sleep(oneSecond);

			// Now at least a second has passed and there is a message on the queue.
			Assert.IsNotNull(cloudQueue.PeekMessage());
		}

		[TestMethod]
		public void Try_AddMessage_of_64K()
		{
			var sixty4K = 64 * 1024; // Max is 65536 UTF8 encoded bytes.

			var message = new StringBuilder("a");

			var cloudQueueMessage = new CloudQueueMessage(message.ToString());

			while (Encoding.Convert(Encoding.Unicode, Encoding.UTF8, cloudQueueMessage.AsBytes).Length <= sixty4K - 2)
			{
				message.Append("a");
				cloudQueueMessage.SetMessageContent(message.ToString());
			}

			var unicodeLength = Encoding.Unicode.GetBytes(message.ToString()).Length;
			var utf8Length = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, cloudQueueMessage.AsBytes).Length;

			Assert.IsTrue(unicodeLength > utf8Length);
			Assert.IsTrue(sixty4K >= utf8Length);
			Assert.IsTrue(utf8Length > 8 * 1024 /* 8 KB */);

			cloudQueue.AddMessage(cloudQueueMessage);
		}

		[TestMethod]
		public void SDK_CreateQueue_AddMessage_ReadMessage_DeleteQueue714641236()
		{
			string queuename = "queuename";
			string message = "some message";

			CloudStorageAccount cloudStorageAccount = CloudStorageAccount.DevelopmentStorageAccount;

			CloudQueueClient cloudQueueClient = new CloudQueueClient(cloudStorageAccount.QueueEndpoint, cloudStorageAccount.Credentials);

			CloudQueue queue = cloudQueueClient.GetQueueReference(queuename);

			queue.CreateIfNotExist();

			queue.AddMessage(new CloudQueueMessage(message));

			var cloudQueueMessage = queue.GetMessage(TimeSpan.FromSeconds(10));

			Assert.AreEqual(message, cloudQueueMessage.AsString);

			queue.DeleteMessage(cloudQueueMessage);

			queue.Delete();
		}

		[TestMethod]
		public void CreateQueue_AddMessage_ReadMessage_DeleteQueue1421909952()
		{
			string queuename = "queuename";
			const string TestClassDataContextName = "FooTestContext this is used in the test class";
			var expectedPayload = new A { Foo = "testing" };

			var cloudStorageAccountManager = new DevelopmentStorageCloudStorageAccountManager();

			ICloudStorageAccount cloudStorageAccount = cloudStorageAccountManager.GetStorageAccount(TestClassDataContextName);
			ICloudQueueClient cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient();

			ICloudQueue queue = cloudQueueClient.GetQueueReference(queuename);

			queue.CreateIfNotExist();

			queue.AddMessage(expectedPayload);

			CloudQueueMessage<A> cloudQueueMessage = queue.GetMessageForSeconds<A>(10);

			Assert.IsInstanceOfType(cloudQueueMessage.Payload(), typeof(A));
// ReSharper disable RedundantCast
			Assert.AreEqual((object)expectedPayload.Bar, (object)cloudQueueMessage.Payload().Bar);
// ReSharper restore RedundantCast

			queue.DeleteMessage(cloudQueueMessage);

			queue.Delete();
		}

		public class A
		{
			public string Foo { get; set; }

			public int Bar { get; set; }

			public bool Baz { get; set; }

			public C Qux { get; set; }
		}

		public class B
		{
			public string Foo { get; set; }

			public int Bar { get; set; }

			public bool Baz { get; set; }

			public D Qux { get; set; }
		}

		public class C
		{
			public DateTime Bar { get; set; }
		}

		public struct D
		{
			public DateTime Bar { get; set; }

			public string Baz { get; set; }
		}

		[TestMethod]
		public void Use_Queue_with_JSON_serialized_messages1830608783()
		{
			var a = new A
			{
				Foo = "something",
				Bar = 42,
				Baz = true,
				Qux = new C
				{
					Bar = DateTime.Now
				}
			};

			var cloudStorageAccount = CloudStorageAccount.DevelopmentStorageAccount;
			var cloudQueueClient = new CloudQueueClient(cloudStorageAccount.QueueEndpoint, cloudStorageAccount.Credentials);
			var queue = cloudQueueClient.GetQueueReference("queuename");

			queue.CreateIfNotExist();

			CloudQueueMessage enqueueMessage = a.AsMessage();

			queue.AddMessage(enqueueMessage);

			CloudQueueMessage dequeueMessage = queue.GetMessage(TimeSpan.FromSeconds(2));

			queue.Delete();

			B b = dequeueMessage.Payload<B>();

			Assert.AreEqual("something", b.Foo);
			Assert.AreEqual(42, b.Bar);
			Assert.AreEqual(true, b.Baz);
			Assert.IsNotNull(b.Qux);
			Assert.IsInstanceOfType(b.Qux, typeof(D));
			Assert.IsInstanceOfType(b.Qux.Bar, typeof(DateTime));
			Assert.IsNull(b.Qux.Baz);
		}
	}
}